/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package xenon3d.scene;

import javax.media.opengl.GL2;
import javax.media.opengl.glu.GLU;
import xenon3d.View3D;

/**
 * A GraphicsContext3D object is used for immediate mode rendering into a 3D
 * canvas. It defines methods to set 3D graphics state and draw 3D geometric
 * primitives. There are no public constructors of GraphicsContext3D. An
 * application obtains a 3D graphics context object from the Canvas3D object
 * that the application wishes to render into by using the getGraphicsContext3D
 * method. A new graphics context is created if one does not already exist.
 *
 * @author Volker Everts
 * @version 0.1 - 06.11.2011: Created
 */
public class GraphicsContext3D extends View3D.GLContext3D {

    // <editor-fold defaultstate="collapsed" desc=" Static Attributes ">

    /** The GraphicsContext3D singleton object. */
    private static GraphicsContext3D gc = new GraphicsContext3D();

    /**
     * Returns the GraphicsContext3D singleton object, or null, if no OpenGL
     * context object is current (i.e. when called from outside one of the
     * RenderListener methods).
     * @return the GraphicsContext3D singleton object
     */
    public static GraphicsContext3D getGC() {
        if (gl == null) return null;
        return gc;
    }

    // </editor-fold>

    // <editor-fold defaultstate="collapsed" desc=" Private Fields ">

    /** The relative transformation reference count. */
    private int relativeRef;

    // </editor-fold>

    // <editor-fold defaultstate="collapsed" desc=" Initialization ">

    /**
     * Singleton object, therefore private constructor.
     */
    private GraphicsContext3D() {}

    // </editor-fold>

    // <editor-fold defaultstate="collapsed" desc=" Public Properties ">

    /**
     * Returns the OpenGL interface object.
     * @return the current GL object
     */
    public GL2 getGL() {
        return gl;
    }

    /**
     * Returns the OpenGL GLU object.
     * @return the current GLU object
     */
    public GLU getGLU() {
        return glu;
    }

    // </editor-fold>

    // <editor-fold defaultstate="collapsed" desc=" Public Methods ">

    /**
     * Begins a new relative transform block. If you want to use relative
     * transforms, you must use this method call and the corresponding
     * endTransform() method call prior to all setTransform() and draw() calls
     * that make up a local object. beginTransform()...endTransform() blocks
     * may be nested as needed.<p>
     * Technically this method pushes the current OpenGL ModelView matrix stack
     * down by one, duplicating the current matrix. That is, after this method
     * call, the matrix on the top of the stack is identical to the one below it.
     */
    public void beginTransform() {
        relativeRef++;
        if (relativeRef > 32) throw new IllegalStateException("Transform Stack Overflow");
        gl.glPushMatrix();
    }

    /**
     * End a relative transform path. Must be matched by a prior call to
     * beginTransform().<p>
     * Technically this method call pops the current OpenGL ModelView matrix
     * stack, replacing the current matrix with the one below it on the stack.
     * Initially, the stack contains one matrix, an identity matrix.
     */
     public void endTransform() {
        relativeRef--;
        if (relativeRef < 0) throw new IllegalStateException("Transform Stack Underflow");
        gl.glPopMatrix();
    }

    /**
     * Applies the specified transform.
      * @param trans the transform to apply
      */
    public void setTransform(Transform trans) {
        trans.apply(gl);
    }

    /**
     * Applies the specified appearance object.
     * @param app the appearance to apply
     */
    public void apply(Appearance app) {
        app.apply(gl);
    }

    /**
     * Draws the specified geometry object.
     * @param geo the geometry to draw
     */
    public void draw(Geometry geo) {
        geo.draw(gl);
    }

//    /**
//     * Renders the specified SceneNode.
//     * @param node the scene node to render
//     */
//    public void render(SceneNode node) {
//        node.render(gl);
//    }
//
//    /**
//     * Animates the specified SceneNode.
//     * @param node the scene node to animate
//     */
//    public void animate(SceneNode node) {
//        node.onAnimate(time);
//    }

    // </editor-fold>

} // end class GraphicsContext3D